// SDL_02 [SDL-OpenGL Gears].nova /* * 3-D gear wheels. This program is in the public domain. * * Brian Paul */ /* Conversion to Nova by Robert Platt */ // Using namespace declarations. using Library.Math; using Library.OpenGL; using Library.SDL; // The application class. class SdlOpenGLGearsApp { // SDL data members. private static int SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_DEPTH; private static uint videoFlags; // Video flags for the "createWindow" function. // "gears" data members. private static float M_PI; private static float view_rotx, view_roty, view_rotz; private static uint gear1, gear2, gear3; private static float angle; // Application class's "main" function. public static void main( String[] args ) { // Initialize the class's static data members. SCREEN_WIDTH = 640; // We want our screen width 640 pixels. SCREEN_HEIGHT = 640; // We want our screen height 480 pixels. SCREEN_DEPTH = 32; // We want 32 bits per pixel. // Initialize the "gears" data members. M_PI = 3.14159265f; view_rotx = 20.0f; view_roty = 30.0f; view_rotz = 0.0f; angle = 0.0f; // Output the keyboard contols. Stream.write( "Keyboard controls:\n" + " left = rotate y-axis +ve\n" + " right = rotate y-axis -ve\n" + " up = rotate x-axis +ve\n" + " down = rotate x-axis -ve\n" + " z = rotate z-axis +ve\n" + " shift + z = rotate z-axis -ve\n" + " esc = exit\n" ); // Initialize SDL. if ( SDL.SDL_Init( SDL.SDL_INIT_VIDEO ) < 0 ) { // Report an error if it fails. Stream.writeLine( "Failed initializing SDL Video: " + SDL.SDL_GetError( ) ); // Return instead of SDL_Quit because SDL isn't yet initialized. return; } // Enable key repeat. SDL.SDL_EnableKeyRepeat( 1, 1 ); // Setup the OpenGL pixel format with SDL. if ( setupPixelFormat( ) == false ) { SDL.SDL_Quit( ); // Shutdown SDL. return; // Exit the application. } // Create the window, video flags are also required. if ( createWindow( "SDL_02 [SDL-OpenGL Gears]", SCREEN_WIDTH, SCREEN_HEIGHT, videoFlags ) == false ) { SDL.SDL_Quit( ); // Shutdown SDL. return; // Exit the application. } // Initialize the OpenGL drawing surface using the resize viewport function. init( ); reshape( SCREEN_WIDTH, SCREEN_HEIGHT ); // Run the main rendering and event handling loop. mainLoop( ); // Shutdown SDL. SDL.SDL_Quit( ); } /** Draw a gear wheel. You'll probably want to call this function when building a display list since we do a lot of trig here. Input: inner_radius - radius of hole at center outer_radius - radius at center of teeth width - width of gear teeth - number of teeth tooth_depth - depth of tooth **/ private static void gear( float inner_radius, float outer_radius, float width, int teeth, float tooth_depth ) { int i; float r0, r1, r2; float angle, da; float u, v, len; r0 = inner_radius; r1 = outer_radius - tooth_depth / 2.0f; r2 = outer_radius + tooth_depth / 2.0f; da = 2.0f * M_PI / teeth / 4.0f; OpenGL.glShadeModel( OpenGL.GL_FLAT ); OpenGL.glNormal3f( 0.0f, 0.0f, 1.0f ); /* draw front face */ OpenGL.glBegin( OpenGL.GL_QUAD_STRIP ); for ( i = 0; i <= teeth; i++ ) { angle = (float)i * 2.0f * M_PI / teeth; OpenGL.glVertex3f( r0 * Math.cos( angle ), r0 * Math.sin( angle ), width * 0.5f ); OpenGL.glVertex3f( r1 * Math.cos( angle ), r1 * Math.sin( angle ), width * 0.5f ); OpenGL.glVertex3f( r0 * Math.cos( angle ), r0 * Math.sin( angle ), width * 0.5f ); OpenGL.glVertex3f( r1 * Math.cos( angle + 3.0f * da ), r1 * Math.sin( angle + 3.0f * da ), width * 0.5f ); } OpenGL.glEnd(); /* draw front sides of teeth */ OpenGL.glBegin( OpenGL.GL_QUADS ); da = 2.0f * M_PI / teeth / 4.0f; for ( i = 0; i < teeth; i++ ) { angle = (float)i * 2.0f * M_PI / teeth; OpenGL.glVertex3f( r1 * Math.cos( angle ), r1 * Math.sin( angle ), width * 0.5f ); OpenGL.glVertex3f( r2 * Math.cos( angle + da ), r2 * Math.sin( angle + da ), width * 0.5f ); OpenGL.glVertex3f( r2 * Math.cos( angle + 2.0f * da ), r2 * Math.sin( angle + 2.0f * da ), width * 0.5f ); OpenGL.glVertex3f( r1 * Math.cos( angle + 3.0f * da ), r1 * Math.sin( angle + 3.0f * da ), width * 0.5f ); } OpenGL.glEnd( ); OpenGL.glNormal3f( 0.0f, 0.0f, -1.0f ); /* draw back face */ OpenGL.glBegin( OpenGL.GL_QUAD_STRIP ); for ( i = 0; i <= teeth; i++ ) { angle = (float)i * 2.0f * M_PI / teeth; OpenGL.glVertex3f( r1 * Math.cos( angle ), r1 * Math.sin( angle ), -width * 0.5f ); OpenGL.glVertex3f( r0 * Math.cos( angle ), r0 * Math.sin( angle ), -width * 0.5f ); OpenGL.glVertex3f( r1 * Math.cos( angle + 3.0f * da ), r1 * Math.sin( angle + 3.0f * da ), -width * 0.5f ); OpenGL.glVertex3f( r0 * Math.cos( angle ), r0 * Math.sin( angle ), -width * 0.5f ); } OpenGL.glEnd( ); /* draw back sides of teeth */ OpenGL.glBegin( OpenGL.GL_QUADS ); da = 2.0f * M_PI / teeth / 4.0f; for ( i = 0; i < teeth; i++ ) { angle = (float)i * 2.0f * M_PI / teeth; OpenGL.glVertex3f( r1 * Math.cos( angle + 3.0f * da ), r1 * Math.sin( angle + 3.0f * da ), -width * 0.5f ); OpenGL.glVertex3f( r2 * Math.cos( angle + 2.0f * da ), r2 * Math.sin( angle + 2.0f * da ), -width * 0.5f ); OpenGL.glVertex3f( r2 * Math.cos( angle + da ), r2 * Math.sin( angle + da ), -width * 0.5f ); OpenGL.glVertex3f( r1 * Math.cos( angle ), r1 * Math.sin( angle ), -width * 0.5f ); } OpenGL.glEnd( ); /* draw outward faces of teeth */ OpenGL.glBegin( OpenGL.GL_QUAD_STRIP ); for ( i = 0; i < teeth; i++ ) { angle = (float)i * 2.0f * M_PI / teeth; OpenGL.glVertex3f( r1 * Math.cos( angle ), r1 * Math.sin( angle ), width * 0.5f ); OpenGL.glVertex3f( r1 * Math.cos( angle ), r1 * Math.sin( angle ), -width * 0.5f ); u = r2 * Math.cos( angle + da ) - r1 * Math.cos( angle ); v = r2 * Math.sin( angle + da ) - r1 * Math.sin( angle ); len = Math.sqrt( u * u + v * v ); u /= len; v /= len; OpenGL.glNormal3f( v, -u, 0.0f ); OpenGL.glVertex3f( r2 * Math.cos( angle + da ), r2 * Math.sin( angle + da ), width * 0.5f ); OpenGL.glVertex3f( r2 * Math.cos( angle + da ), r2 * Math.sin( angle + da ), -width * 0.5f ); OpenGL.glNormal3f( Math.cos( angle ), Math.sin( angle ), 0.0f ); OpenGL.glVertex3f( r2 * Math.cos( angle + 2.0f * da ), r2 * Math.sin( angle + 2.0f * da ), width * 0.5f ); OpenGL.glVertex3f( r2 * Math.cos( angle + 2.0f * da ), r2 * Math.sin( angle + 2.0f * da ), -width * 0.5f ); u = r1 * Math.cos( angle + 3.0f * da ) - r2 * Math.cos( angle + 2.0f * da ); v = r1 * Math.sin( angle + 3.0f * da ) - r2 * Math.sin( angle + 2.0f * da ); OpenGL.glNormal3f( v, -u, 0.0f ); OpenGL.glVertex3f( r1 * Math.cos( angle + 3.0f * da ), r1 * Math.sin( angle + 3.0f * da ), width * 0.5f ); OpenGL.glVertex3f( r1 * Math.cos( angle + 3.0f * da ), r1 * Math.sin( angle + 3.0f * da ), -width * 0.5f ); OpenGL.glNormal3f( Math.cos( angle ), Math.sin( angle ), 0.0f ); } OpenGL.glVertex3f( r1 * Math.cos( 0.0f ), r1 * Math.sin( 0.0f ), width * 0.5f ); OpenGL.glVertex3f( r1 * Math.cos( 0.0f ), r1 * Math.sin( 0.0f ), -width * 0.5f ); OpenGL.glEnd( ); OpenGL.glShadeModel( OpenGL.GL_SMOOTH ); /* draw inside radius cylinder */ OpenGL.glBegin( OpenGL.GL_QUAD_STRIP ); for ( i = 0; i <= teeth; i++ ) { angle = (float)i * 2.0f * M_PI / teeth; OpenGL.glNormal3f( -Math.cos( angle ), -Math.sin( angle ), 0.0f ); OpenGL.glVertex3f( r0 * Math.cos( angle ), r0 * Math.sin( angle ), -width * 0.5f ); OpenGL.glVertex3f( r0 * Math.cos( angle ), r0 * Math.sin( angle ), width * 0.5f ); } OpenGL.glEnd( ); } private static void draw( ) { OpenGL.glClear( OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT ); OpenGL.glPushMatrix( ); OpenGL.glRotatef( view_rotx, 1.0f, 0.0f, 0.0f ); OpenGL.glRotatef( view_roty, 0.0f, 1.0f, 0.0f ); OpenGL.glRotatef( view_rotz, 0.0f, 0.0f, 1.0f ); OpenGL.glPushMatrix( ); OpenGL.glTranslatef( -3.0f, -2.0f, 0.0f ); OpenGL.glRotatef( angle, 0.0f, 0.0f, 1.0f ); OpenGL.glCallList( gear1 ); OpenGL.glPopMatrix( ); OpenGL.glPushMatrix( ); OpenGL.glTranslatef( 3.1f, -2.0f, 0.0f ); OpenGL.glRotatef( -2.0f * angle - 9.0f, 0.0f, 0.0f, 1.0f ); OpenGL.glCallList( gear2 ); OpenGL.glPopMatrix( ); OpenGL.glPushMatrix( ); OpenGL.glTranslatef( -3.1f, 4.2f, 0.0f ); OpenGL.glRotatef( -2.0f * angle - 25.0f, 0.0f, 0.0f, 1.0f ); OpenGL.glCallList( gear3 ); OpenGL.glPopMatrix( ); OpenGL.glPopMatrix( ); // Swap the buffers. SDL.SDL_GL_SwapBuffers( ); } private static void init( ) { float[] pos = { 5.0f, 5.0f, 10.0f, 0.0f }; float[] red = { 0.8f, 0.1f, 0.0f, 1.0f }; float[] green = { 0.0f, 0.8f, 0.2f, 1.0f }; float[] blue = { 0.2f, 0.2f, 1.0f, 1.0f }; OpenGL.glLightfv( OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, pos ); OpenGL.glEnable( OpenGL.GL_CULL_FACE ); OpenGL.glEnable( OpenGL.GL_LIGHTING ); OpenGL.glEnable( OpenGL.GL_LIGHT0 ); OpenGL.glEnable( OpenGL.GL_DEPTH_TEST ); /* make the gears */ gear1 = OpenGL.glGenLists( 1 ); OpenGL.glNewList( gear1, OpenGL.GL_COMPILE ); OpenGL.glMaterialfv( OpenGL.GL_FRONT, OpenGL.GL_AMBIENT_AND_DIFFUSE, red ); gear( 1.0f, 4.0f, 1.0f, 20, 0.7f ); OpenGL.glEndList( ); gear2 = OpenGL.glGenLists( 1 ); OpenGL.glNewList( gear2, OpenGL.GL_COMPILE ); OpenGL.glMaterialfv( OpenGL.GL_FRONT, OpenGL.GL_AMBIENT_AND_DIFFUSE, green ); gear( 0.5f, 2.0f, 2.0f, 10, 0.7f ); OpenGL.glEndList( ); gear3 = OpenGL.glGenLists( 1 ); OpenGL.glNewList( gear3, OpenGL.GL_COMPILE ); OpenGL.glMaterialfv( OpenGL.GL_FRONT, OpenGL.GL_AMBIENT_AND_DIFFUSE, blue ); gear( 1.3f, 2.0f, 0.5f, 10, 0.7f ); OpenGL.glEndList( ); OpenGL.glEnable( OpenGL.GL_NORMALIZE ); } /* new window size or exposure */ private static void reshape( int width, int height ) { // Prevent a divide by zero error. if ( width == 0 ) { // Make the height equal to one if zero. width = 1; } double h = (double)height / (double)width; OpenGL.glViewport( 0, 0, width, height ); OpenGL.glMatrixMode( OpenGL.GL_PROJECTION ); OpenGL.glLoadIdentity( ); OpenGL.glFrustum( -1.0, 1.0, -h, h, 5.0, 60.0 ); OpenGL.glMatrixMode( OpenGL.GL_MODELVIEW ); OpenGL.glLoadIdentity( ); OpenGL.glTranslatef( 0.0f, 0.0f, -40.0f ); } // Sets the pixel format for openGL and video flags for SDL. private static bool setupPixelFormat( ) { // We set the common flags here. videoFlags = SDL.SDL_OPENGL; // It's an openGL window. videoFlags |= SDL.SDL_HWPALETTE; // Exclusive access to hardware colour palette. videoFlags |= SDL.SDL_RESIZABLE; // The window must be resizeable. // Query SDL for information about our video hardware. SDL_VideoInfo videoInfo = SDL.SDL_GetVideoInfo( ); // Check for a "null" return object. if ( videoInfo == null ) { return false; } // Set the system dependant flags. // Is it a hardware surface? if( videoInfo.hw_available == 1 ) { videoFlags |= SDL.SDL_HWSURFACE; } else { videoFlags |= SDL.SDL_SWSURFACE; } // Is hardware blitting available? if ( videoInfo.blit_hw == 1 ) { videoFlags |= SDL.SDL_HWACCEL; } // Tell SDL that the OpenGL rendering is going to be double buffered. SDL.SDL_GL_SetAttribute( SDL.SDL_GL_DOUBLEBUFFER, 1 ); // Specify the size of depth buffer. SDL.SDL_GL_SetAttribute( SDL.SDL_GL_DEPTH_SIZE, SCREEN_DEPTH ); // We aren't going to use the stencil buffer. SDL.SDL_GL_SetAttribute( SDL.SDL_GL_STENCIL_SIZE, 0 ); // The next four lines set the bits allocated per pixel - // - for the accumulation buffer to zero. SDL.SDL_GL_SetAttribute( SDL.SDL_GL_ACCUM_RED_SIZE, 0 ); SDL.SDL_GL_SetAttribute( SDL.SDL_GL_ACCUM_GREEN_SIZE, 0 ); SDL.SDL_GL_SetAttribute( SDL.SDL_GL_ACCUM_BLUE_SIZE, 0 ); SDL.SDL_GL_SetAttribute( SDL.SDL_GL_ACCUM_ALPHA_SIZE, 0 ); return true; } // Create the OpenGL window. private static bool createWindow( String windowName, int width, int height, uint videoFlags ) { // SCREEN_DEPTH is a constant for bits per pixel. if ( SDL.SDL_SetVideoMode( width, height, SCREEN_DEPTH, videoFlags ) == null ) { return false; // Report an error. } // Set the window caption (1st arg) and the icon caption (2nd arg). SDL.SDL_WM_SetCaption( windowName, windowName ); return true; } private static void mainLoop( ) { // Local declarations. bool eventLoopActive = true; SDL_Event event = new SDL_Event( ); // Event handling loop. while ( eventLoopActive ) { // Look for events (like keystrokes, resizing etc.). while( SDL.SDL_PollEvent( event ) == 1 ) { // If user wishes to quit. if ( event.typeID == SDL.SDL_QUIT ) { // This implies our job is done. eventLoopActive = false; } // If there is a resize event. if ( event.typeID == SDL.SDL_VIDEORESIZE ) { // Request SDL to resize the window to the size - // - and depth etc. that we specify. if ( SDL.SDL_SetVideoMode( event.resize.w, event.resize.h, SCREEN_DEPTH, videoFlags ) == null ) { // Exit if there is an error. return; } // Resize the OpenGL viewport. // SDL PROBLEM: init( ) should not have to be called here. // Seems that the "SDL_SetVideoMode" function is reseting the OpenGL context. // Other OpenGL libraries like CPW and GLUT do not have this problem. init( ); reshape( event.resize.w, event.resize.h ); } // Check for keyboard events. if ( event.typeID == SDL.SDL_KEYDOWN ) { if ( event.key.keysym.sym == SDL.SDLK_ESCAPE ) { eventLoopActive = false; } if ( event.key.keysym.sym == SDL.SDLK_UP ) { view_rotx += 5.0f; } if ( event.key.keysym.sym == SDL.SDLK_DOWN ) { view_rotx -= 5.0f; } if ( event.key.keysym.sym == SDL.SDLK_LEFT ) { view_roty += 5.0f; } if ( event.key.keysym.sym == SDL.SDLK_RIGHT ) { view_roty -= 5.0f; } if ( event.key.keysym.sym == SDL.SDLK_z ) { // Brackets are needed here for the bitwise AND operator - // - because the equality operator has a slightly higher precedence. // This is also the same for C++. if ( ( SDL.SDL_GetModState( ) & SDL.KMOD_SHIFT ) != 0 ) { view_rotz -= 5.0f; } else { view_rotz += 5.0f; } } } } // Render a frame of the OpenGL scene. draw( ); // Increment the angle of the gears. angle += 1.0f; } } }